/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ua.yandex.prodcons.threads;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Neophron
 */
public class RingBuffer {

    private final int nN;
    private int oldest, newest;
    private boolean isEmpty = true;
    private boolean isFull = false;
    private final List<Integer> elements;

    public RingBuffer(int nN) {
        this.nN = nN;
        oldest = 0;
        newest = nN - 1;
        elements = new ArrayList<>(nN);
        for (int i = 0; i < nN; i++) {
            elements.add(null);
        }
    }

    private int next(int n) {
        int r = n;
        r++;
        if (r == nN) {
            return 0;
        } else {
            return r;
        }
    }

    //Но ведь synchronized метод гарантирует наличие только одного потока внутри
    //защищенного кода. И после isEmpty один из потоков заблокирует метод, а 
    //остальные будут ждать его завершения. 
    //Где я заблуждаюсь?
    public synchronized int take() {
        while (isEmpty) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        int res = elements.get(oldest);
        elements.set(oldest, null);
        oldest = next(oldest);
        if (elements.get(oldest) == null) {
            isEmpty = true;
        }
        if (isFull) {
            isFull = false;
            notifyAll();
        }
        return res;
    }

    public synchronized void put(int element) {
        while (isFull) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        newest = next(newest);
        elements.set(newest, element);
        if (next(newest) == oldest) {
            isFull = true;
        }
        if (isEmpty) {
            isEmpty = false;
            notifyAll();
        }
    }
}
